//*****************************************************************************
//
// qs_ek-lm3s811.c - The quick start application for the LM3S811 Evaluation
//                   Board.
//
// Copyright (c) 2006-2007 Luminary Micro, Inc.  All rights reserved.
// 
// Software License Agreement
// 
// Luminary Micro, Inc. (LMI) is supplying this software for use solely and
// exclusively on LMI's microcontroller products.
// 
// The software is owned by LMI and/or its suppliers, and is protected under
// applicable copyright laws.  All rights are reserved.  Any use in violation
// of the foregoing restrictions may subject the user to criminal sanctions
// under applicable laws, as well as to civil liability for the breach of the
// terms and conditions of this license.
// 
// THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
// OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
// LMI SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR
// CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
// 
// This is part of revision 1716 of the Stellaris Peripheral Driver Library.
//
//*****************************************************************************

#include "hw_ints.h"
#include "hw_memmap.h"
#include "hw_types.h"
#include "adc.h"
#include "debug.h"
#include "gpio.h"
#include "interrupt.h"
#include "sysctl.h"
#include "timer.h"
#include "uart.h"
#include "osram96x16x1.h"
#include "game.h"
#include "globals.h"
#include "random.h"
#include "screen_saver.h"

//*****************************************************************************
//
//! \addtogroup ek_lm3s811_list
//! <h1>EK-LM3S811 Quickstart Application (qs_ek-lm3s811)</h1>
//!
//! A game in which a ship is navigated through an endless tunnel.  The
//! potentiometer is used to move the ship up and down, and the user push
//! button is used to fire a missile to destroy obstacles in the tunnel.  Score
//! accumulates for survival and for destroying obstacles.  The game lasts for
//! only one ship; the score is displayed on the virtual UART at 115,200, 8-N-1
//! during game play and will be displayed on the screen at the end of the
//! game.
//!
//! Since the OLED display on the evaluation board has burn-in characteristics
//! similar to a CRT, the application also contains a screen saver.  The screen
//! saver will only become active if two minutes have passed without the user
//! push button being pressed while waiting to start the game (i.e. it will
//! never come on during game play).  An implementation of the Game of Life is
//! run with a field of random data as the seed value.
//!
//! After two minutes of running the screen saver, the display will be turned
//! off and the user LED will blink.  Either mode of screen saver (Game of Life
//! or blank display) will be exited by pressing the user push button.  The
//! button will then need to be pressed again to start the game.
//
//*****************************************************************************

//*****************************************************************************
//
// A set of flags used to track the state of the application.
//
//*****************************************************************************
unsigned long g_ulFlags;

//*****************************************************************************
//
// The current filtered value of the potentiometer.
//
//*****************************************************************************
unsigned long g_ulWheel;

//*****************************************************************************
//
// Storage for a local frame buffer.
//
//*****************************************************************************
unsigned char g_pucFrame[192];

//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void
__error__(char *pcFilename, unsigned long ulLine)
{
}
#endif

//*****************************************************************************
//
// The interrupt handler for the ADC interrupt.
//
//*****************************************************************************
void
ADCIntHandler(void)
{
    unsigned long ulData;

    //
    // Clear the ADC interrupt.
    //
    ADCIntClear(ADC_BASE, 3);

    //
    // Read the data from the ADC.
    //
    ADCSequenceDataGet(ADC_BASE, 3, &ulData);

    //
    // Add the ADC data to the random number entropy pool.
    //
    RandomAddEntropy(ulData);

    //
    // Pass the ADC data through the low pass filter (with a coefficient of
    // 0.9) to update the position of the potentiometer.
    //
    g_ulWheel = ((g_ulWheel * 58982) + (ulData * 6554)) / 65536;

    //
    // Read the push button.
    //
    ulData = GPIOPinRead(GPIO_PORTC_BASE, PUSH_BUTTON) ? 1 : 0;

    //
    // See if the push button state doesn't match the debounced push button
    // state.
    //
    if(ulData != HWREGBITW(&g_ulFlags, FLAG_BUTTON))
    {
        //
        // Increment the debounce counter.
        //
        HWREGBITW(&g_ulFlags, FLAG_DEBOUNCE_LOW) ^= 1;
        if(!HWREGBITW(&g_ulFlags, FLAG_DEBOUNCE_LOW))
        {
            HWREGBITW(&g_ulFlags, FLAG_DEBOUNCE_HIGH) = 1;
        }

        //
        // See if the debounce counter has reached three.
        //
        if(HWREGBITW(&g_ulFlags, FLAG_DEBOUNCE_LOW) &&
           HWREGBITW(&g_ulFlags, FLAG_DEBOUNCE_HIGH))
        {
            //
            // The button has been in the new state for three consecutive
            // samples, so it has been debounced.  Toggle the debounced state
            // of the button.
            //
            HWREGBITW(&g_ulFlags, FLAG_BUTTON) ^= 1;

            //
            // If the button was just pressed, set the flag to indicate that
            // fact.
            //
            if(HWREGBITW(&g_ulFlags, FLAG_BUTTON) == 0)
            {
                HWREGBITW(&g_ulFlags, FLAG_BUTTON_PRESS) = 1;
            }
        }
    }
    else
    {
        //
        // Since the button state matches the debounced state, reset the
        // debounce counter.
        //
        HWREGBITW(&g_ulFlags, FLAG_DEBOUNCE_LOW) = 0;
        HWREGBITW(&g_ulFlags, FLAG_DEBOUNCE_HIGH) = 0;
    }

    //
    // Increment the clock count.
    //
    HWREGBITW(&g_ulFlags, FLAG_CLOCK_COUNT_LOW) ^= 1;
    if(!HWREGBITW(&g_ulFlags, FLAG_CLOCK_COUNT_LOW))
    {
        HWREGBITW(&g_ulFlags, FLAG_CLOCK_COUNT_HIGH) ^= 1;
    }

    //
    // If the clock count has wrapped around to zero, then set a flag to
    // indicate that the display needs to be updated.
    //
    if((HWREGBITW(&g_ulFlags, FLAG_CLOCK_COUNT_LOW) == 0) &&
       (HWREGBITW(&g_ulFlags, FLAG_CLOCK_COUNT_HIGH) == 0))
    {
        HWREGBITW(&g_ulFlags, FLAG_UPDATE) = 1;
    }

    //
    // Indicate that a timer interrupt has occurred.
    //
    HWREGBITW(&g_ulFlags, FLAG_CLOCK_TICK) = 1;
}

//*****************************************************************************
//
// The main code for the application.  It sets up the peripherals, displays the
// splash screens, and then manages the interaction between the game and the
// screen saver.
//
//*****************************************************************************
int
main(void)
{
    //
    // Set the clocking to run at 20MHz from the PLL.
    //
    SysCtlClockSet(SYSCTL_SYSDIV_10 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
                   SYSCTL_XTAL_6MHZ);

    //
    // Enable the peripherals used by the application.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

    //
    // Configure the ADC to sample the potentiometer when the timer expires.
    // After sampling, the ADC will interrupt the processor; this is used as
    // the heartbeat for the game.
    //
    ADCSequenceConfigure(ADC_BASE, 3, ADC_TRIGGER_TIMER, 0);
    ADCSequenceStepConfigure(ADC_BASE, 3, 0,
                             ADC_CTL_CH0 | ADC_CTL_IE | ADC_CTL_END);
    ADCSequenceEnable(ADC_BASE, 3);
    ADCIntEnable(ADC_BASE, 3);
    IntEnable(INT_ADC3);

    //
    // Configure the first timer to generate a 10 kHz PWM signal for driving
    // the user LED.
    //
    TimerConfigure(TIMER0_BASE, TIMER_CFG_16_BIT_PAIR | TIMER_CFG_B_PWM);
    TimerLoadSet(TIMER0_BASE, TIMER_B, (SysCtlClockGet() / 10000) - 1);
    TimerMatchSet(TIMER0_BASE, TIMER_B, 0);
    TimerControlLevel(TIMER0_BASE, TIMER_B, true);
    TimerEnable(TIMER0_BASE, TIMER_B);

    //
    // Configure the second timer to generate triggers to the ADC to sample the
    // potentiometer.
    //
    TimerConfigure(TIMER1_BASE, TIMER_CFG_32_BIT_PER);
    TimerLoadSet(TIMER1_BASE, TIMER_A, SysCtlClockGet() / 120);
    TimerControlStall(TIMER1_BASE, TIMER_A, true);
    TimerControlTrigger(TIMER1_BASE, TIMER_A, true);
    TimerEnable(TIMER1_BASE, TIMER_A);

    //
    // Configure the LED, push button, and UART GPIOs as required.
    //
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    GPIOPinTypeGPIOInput(GPIO_PORTC_BASE, PUSH_BUTTON);
    GPIOPinTypeGPIOOutput(GPIO_PORTC_BASE, USER_LED);
    GPIOPinWrite(GPIO_PORTC_BASE, USER_LED, 0);

    //
    // Configure the first UART for 115,200, 8-N-1 operation.
    //
    UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 115200,
                        (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
                         UART_CONFIG_PAR_NONE));
    UARTEnable(UART0_BASE);

    //
    // Send a welcome message to the UART.
    //
    UARTCharPut(UART0_BASE, 'L');
    UARTCharPut(UART0_BASE, 'M');
    UARTCharPut(UART0_BASE, '3');
    UARTCharPut(UART0_BASE, 'S');
    UARTCharPut(UART0_BASE, '8');
    UARTCharPut(UART0_BASE, '1');
    UARTCharPut(UART0_BASE, '1');
    UARTCharPut(UART0_BASE, '\r');
    UARTCharPut(UART0_BASE, '\n');

    //
    // Initialize the OSRAM OLED display.
    //
    OSRAM96x16x1Init(true);

    //
    // Throw away any button presses that may have occurred while the splash
    // screens were being displayed.
    //
    HWREGBITW(&g_ulFlags, FLAG_BUTTON_PRESS) = 0;

    //
    // Loop forever.
    //
    while(1)
    {
        //
        // Display the main screen.
        //
        if(MainScreen())
        {
            //
            // The button was pressed, so start the game.
            //
            PlayGame();
        }
        else
        {
            //
            // The button was not pressed during the timeout period, so start
            // the screen saver.
            //
            ScreenSaver();
        }
    }
}
